package com.hd.determine.modules.alarm.service.impl;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hd.common.constant.BizConstant;
import com.hd.common.utils.CollectionUtils;
import com.hd.common.utils.DateUtil;
import com.hd.common.utils.GenUtil;
import com.hd.common.utils.ValueUtils;
import com.hd.determine.config.rabbitmq.model.AlarmMQ;
import com.hd.determine.config.rabbitmq.producer.RabbitProducer;
import com.hd.determine.config.redis.prefix.CacheKey;
import com.hd.determine.config.redis.utils.JedisUtil;
import com.hd.determine.modules.alarm.mapper.AlarmDetailMapper;
import com.hd.determine.modules.alarm.model.AlarmDetail;
import com.hd.determine.modules.alarm.model.AlarmRule;
import com.hd.determine.modules.alarm.service.IAlarmDetailService;
import com.hd.determine.modules.alarm.util.TemplateUtils;
import com.hd.determine.modules.vehicle.model.VehicleEndPos;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 报警明细表 服务实现类
 * </p>
 *
 * @author chentl123
 * @since 2020-12-28
 */
@Slf4j
@Service
public class AlarmDetailServiceImpl extends ServiceImpl<AlarmDetailMapper, AlarmDetail> implements IAlarmDetailService {

    public static final int NIGHT_CONTINUOUS_MINUTES = 2;
    public static final int SECONDS_PER_MIN = 60;

    @Resource
    private JedisUtil jedisUtil;

    @Resource
    private RabbitProducer rabbitProducer;

    @Override
    public void generateAlarmData(VehicleEndPos data, AlarmRule alarmRule) {

        Date now = new Date();
        Date lastGpsTime = data.getLastGpsTime();
        Date lastUpdateTime = data.getLastUpdateTime();
        Date continuousDrivingStartTime = data.getContinuousDrivingStartTime();
        String plat_no = data.getPlatNo();
        String plat_no_color = data.getPlatNoColor();
        String deptId = data.getDeptId();
        String alarm_type = data.getBizAlarmType();
        Date gpsTime = data.getGpsTime();
        String gpsTimeStr = DateUtil.formatDate(gpsTime, BizConstant.Common.DATE_FORMAT_SECOND.key());
        String enterpriseName = data.getEnterpriseName();
        String enterpriseNo = data.getEnterpriseNo();
        String regionCode = data.getRegionCode();
        String regionName = data.getRegionName();
        Date nightNoEntryStartTime = data.getNightStartTime();
        BizConstant.AlarmType alarmTypeEnum = BizConstant.AlarmType.get(alarm_type);
        String alarmName = Objects.requireNonNull(BizConstant.AlarmType.get(alarm_type)).val();
        String vehicleType = data.getVehicleType();
        String transType = data.getTransType();

        AlarmDetail insert = null;

        // 不定位 无数据 报警直接插入
        if (BizConstant.AlarmType.OFF_GPS.equals(alarmTypeEnum)) {
            long diffSeconds = ValueUtils.parseLong(DateUtil.getDiffSeconds(gpsTime, lastGpsTime));
            String overSeconds = ValueUtils.parseString(diffSeconds - alarmRule.getExceededMinute() * SECONDS_PER_MIN);
            String alarmDuration = DateUtil.getDateDiffHmsTemplate(overSeconds);
            insert = new AlarmDetail(alarmDuration, plat_no, plat_no_color, vehicleType, transType, gpsTime, enterpriseName, enterpriseNo, regionCode, regionName, alarm_type, lastGpsTime, TemplateUtils.alarmDescTemplate(alarmRule, null, null, gpsTimeStr), BizConstant.HandleStatus.UN_HANDLED.key(), BizConstant.VerifyStatus.UN_VERIFIED.key(), deptId);
            insert.setEndTime(gpsTime);
            //发送到插入队列
            rabbitProducer.sendToAlarmGenerateQueue(AlarmMQ.builder().alarmDetail(insert).build());

            return;
        } else if (BizConstant.AlarmType.OFF_LINE.equals(alarmTypeEnum)) {
            long diffSeconds = ValueUtils.parseLong(DateUtil.getDiffSeconds(now, data.getLastUpdateTime()));
            String overSeconds = ValueUtils.parseString(diffSeconds - alarmRule.getExceededMinute() * SECONDS_PER_MIN);
            String alarmDuration = DateUtil.getDateDiffHmsTemplate(overSeconds);
            insert = new AlarmDetail(alarmDuration, plat_no, plat_no_color, vehicleType, transType, gpsTime, enterpriseName, enterpriseNo, regionCode, regionName, alarm_type, lastGpsTime, TemplateUtils.alarmDescTemplate(alarmRule, null, null, gpsTimeStr), BizConstant.HandleStatus.UN_HANDLED.key(), BizConstant.VerifyStatus.UN_VERIFIED.key(), deptId);
            insert.setEndTime(gpsTime);
            //发送到插入队列
            rabbitProducer.sendToAlarmGenerateQueue(AlarmMQ.builder().alarmDetail(insert).build());

            return;
        }


        String cacheKey = GenUtil.cacheKey(CacheKey.CURRENT_ALARM__KEY, alarmName);
        String filed = BizConstant.VehicleNoColor.get(plat_no_color).val() + BizConstant.Common.UNDERLINE.key() + plat_no;
        String cacheAlarm = ValueUtils.parseString(jedisUtil.hget(cacheKey, filed));


        boolean alarmAlreadyExists = StringUtils.isNotEmpty(cacheAlarm);

        //报警不存在  考虑生成新的报警
        if (!alarmAlreadyExists) {

            if (BizConstant.AlarmType.FATIGUE_DRIVE.equals(alarmTypeEnum)) {
                long diffSeconds = ValueUtils.parseLong(DateUtil.getDiffSeconds(gpsTime, continuousDrivingStartTime));
                String continueDrivingTimeStr = ValueUtils.parseString(diffSeconds);
                String overSeconds = ValueUtils.parseString(diffSeconds - alarmRule.getContinuousMinute() * SECONDS_PER_MIN);
                String alarmDuration = DateUtil.getDateDiffHmsTemplate(overSeconds);
                String alarmDesc = DateUtil.getDateDiffHmsTemplate(continueDrivingTimeStr);
                insert = new AlarmDetail(alarmDuration, plat_no, plat_no_color, vehicleType, transType, gpsTime, enterpriseName, enterpriseNo, regionCode, regionName, alarm_type, continuousDrivingStartTime, TemplateUtils.alarmDescTemplate(alarmRule, null, alarmDesc, null), BizConstant.HandleStatus.UN_HANDLED.key(), BizConstant.VerifyStatus.UN_VERIFIED.key(), deptId);
                //发送到插入队列
                rabbitProducer.sendToAlarmGenerateQueue(AlarmMQ.builder().alarmDetail(insert).build());
            }

            if (BizConstant.AlarmType.NIGHT_NO_ENTRY.equals(alarmTypeEnum)) {
                String endPostCacheKey = GenUtil.cacheKey(CacheKey.END_POSITION_KEY, plat_no, BizConstant.Common.UNDERLINE.key(), plat_no_color);
                if (nightNoEntryStartTime == null) {
                    //向末次位置缓存中 写入夜间行驶开始时间
                    data.setNightStartTime(now);
                    jedisUtil.set(endPostCacheKey, JSON.toJSONString(data), 0);
                    return;
                } else {
                    //连续两分钟以上 触发夜间报警
                    long cndMinutes = ValueUtils.parseLong(DateUtil.getDiffMins(now, nightNoEntryStartTime));
                    if (cndMinutes <= NIGHT_CONTINUOUS_MINUTES) return;
                    String alarmDuration = DateUtil.getDateDiffHmsTemplate(DateUtil.getDiffSeconds(now, gpsTime));
                    insert = new AlarmDetail(alarmDuration, plat_no, plat_no_color, vehicleType, transType, gpsTime, enterpriseName, enterpriseNo, regionCode, regionName, alarm_type, gpsTime, TemplateUtils.alarmDescTemplate(alarmRule, null, null, gpsTimeStr), BizConstant.HandleStatus.UN_HANDLED.key(), BizConstant.VerifyStatus.UN_VERIFIED.key(), deptId);

                    //发送到插入队列
                    rabbitProducer.sendToAlarmGenerateQueue(AlarmMQ.builder().alarmDetail(insert).build());

                    //清理夜间连续驾驶开始时间
                    data.clearNightStartTime();
                    jedisUtil.set(endPostCacheKey, JSON.toJSONString(data), 0);
                }

            }

            //更新缓存
            if (insert != null) jedisUtil.hset(cacheKey, filed, JSON.toJSONString(insert));

            return;
        }

        //报警已存在   考虑更新已存在的报警
        AlarmDetail update = JSON.parseObject(cacheAlarm, AlarmDetail.class);
        Date startTime = update.getStartTime();
        String alarmDuration = null;
        String alarmDesc = null;
        if (BizConstant.AlarmType.FATIGUE_DRIVE.equals(alarmTypeEnum)) {
            long diffSeconds = ValueUtils.parseLong(DateUtil.getDiffSeconds(gpsTime, continuousDrivingStartTime));
            String continueDrivingTimeStr = ValueUtils.parseString(diffSeconds);
            String overSeconds = ValueUtils.parseString(diffSeconds - alarmRule.getContinuousMinute() * SECONDS_PER_MIN);
            alarmDuration = DateUtil.getDateDiffHmsTemplate(overSeconds);
            alarmDesc = TemplateUtils.alarmDescTemplate(alarmRule, null, DateUtil.getDateDiffHmsTemplate(continueDrivingTimeStr), null);

        } else if (BizConstant.AlarmType.NIGHT_NO_ENTRY.equals(alarmTypeEnum)) {
            alarmDuration = DateUtil.getDateDiffHmsTemplate(DateUtil.getDiffSeconds(gpsTime, startTime));
            alarmDesc = TemplateUtils.alarmDescTemplate(alarmRule, null, null, null);
        }

        update.setAlarmDuration(alarmDuration);
        update.setGpsTime(gpsTime);
        update.setAlarmDesc(alarmDesc);

        //发送到更新队列
        rabbitProducer.sendToAlarmUpdateQueue(AlarmMQ.builder().alarmDetail(update).build());

        //更新缓存
        jedisUtil.hset(cacheKey, filed, JSON.toJSONString(update));


    }

    @Override
    public void stopAlarm(VehicleEndPos data, BizConstant.AlarmType alarmTypeEnum) {
        String alarmName = alarmTypeEnum.val();
        String plat_no = data.getPlatNo();
        String plat_no_color = data.getPlatNoColor();
        //3 查缓存
        String cacheKey = GenUtil.cacheKey(CacheKey.CURRENT_ALARM__KEY, plat_no, plat_no_color, BizConstant.Common.COLON.key(), alarmName);
        String cacheAlarm = ValueUtils.parseString(jedisUtil.get(cacheKey, 0));

        if (StringUtils.isEmpty(cacheAlarm)) return;

        Date now = new Date();
        AlarmDetail ca = JSON.parseObject(cacheAlarm, AlarmDetail.class);

        //更新endTime为 gpsTime
        ca.setEndTime(now);
        ca.updateById();

        //删报警缓存
        jedisUtil.del(cacheKey);
    }

    @Transactional(rollbackFor = Exception.class)
    @Async("determineTaskExecutor")
    @Override
    public void multiInsert(AlarmDetail alarmDetail) {
        insertIfNotExists(alarmDetail);
    }

    @Override
    public void multiUpdate(List<AlarmDetail> alarmDetailList) {
        if (CollectionUtils.isNotEmpty(alarmDetailList)) updateBatchById(alarmDetailList);
    }

    @Override
    public void insertIfNotExists(AlarmDetail alarmDetail) {
        this.baseMapper.insertIfNotExists(alarmDetail);
    }
}
